Skip to content

Bind go-ruby-rack and go-ruby-sinatra into rbgo as native modules - #104

Merged
tannevaled merged 1 commit into
mainfrom
bind/web
Jul 2, 2026
Merged

tannevaled merged 1 commit into
mainfrom
bind/web

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Wire go-ruby-rack and go-ruby-sinatra into the embedded Ruby runtime, closing the two web-app gaps the run-conformance harness surfaced: require "rack" and the Sinatra DSL (require "sinatra/base"). Both libraries were CI-green but never bound.

rack (require "rack" / "rack/utils")

  • Rack::RELEASE, Rack::Request (env accessors + parsed params/GET/POST/cookies), Rack::Response (buffered status/headers/body, #finish/#to_a → [status, headers, body]), Rack::Utils (escape/unescape/escape_html/parse_query/build_query/status_code).
  • Deterministic env/query/escape over the library — no socket, no network.

sinatra (require "sinatra/base" / "sinatra")

  • Sinatra::Base with the class-level routing DSL (get/post/put/delete/patch/options/head), before/after filters, not_found / error handlers, set/enable/disable/configure/helpers, and the Rack #call adapter — App.new.call(env) → [status, headers, body] (also a class method).
  • Route/filter blocks run against a request context exposing params, request, response, status, body, headers, content_type, redirect, halt, pass, session, settings. halt/redirect/pass unwind through the library's panic-based control flow, which the dispatcher recovers. The handler-block eval is the rbgo seam; routing, param extraction and dispatch are the go-ruby-sinatra library over go-ruby-rack.
  • Subclasses inherit their ancestors' routes.

Conformance

The webapp run-conformance stage 3 (Sinatra DSL) now RUNS green (hard assertion): GET /hi?n=amy → [200, headers, ["hi amy"]].

Gates

  • rbgo build + full go test ./... green under TZ=UTC.
  • New binding code at 100% coverage under the exact CI gate (-race -coverpkg=$(go list ./internal/... | paste -sd,)).
  • gofmt / vet clean.

🤖 Generated with Claude Code

Wire the go-ruby-rack and go-ruby-sinatra libraries into the embedded Ruby
runtime, closing the two web-app gaps the run-conformance harness surfaced:
`require "rack"` and the Sinatra DSL (`require "sinatra/base"`).

rack (require "rack" / "rack/utils"):
  - Rack::RELEASE, Rack::Request (env accessors + parsed params), Rack::Response
    (buffered status/headers/body + #finish/#to_a triple), Rack::Utils
    (escape/unescape/escape_html/parse_query/build_query/status_code).
  - Deterministic env/query/escape over the library; no socket, no network.

sinatra (require "sinatra/base" / "sinatra"):
  - Sinatra::Base with the class-level routing DSL (get/post/put/delete/patch/
    options/head), before/after filters, not_found / error handlers,
    set/enable/disable/configure/helpers, and the Rack #call adapter
    (App.new.call(env) -> [status, headers, body]) available as both an instance
    and a class method.
  - Route/filter blocks run against a SinatraCtx exposing params, request,
    response, status, body, headers, content_type, redirect, halt, pass, session
    and settings; halt/redirect/pass unwind through the library's panic-based
    control flow, which the dispatcher recovers. The handler-block eval is the
    rbgo seam; routing, param extraction and dispatch are the go-ruby-sinatra
    library over go-ruby-rack.
  - Subclasses inherit their ancestors' routes; per-class declarations are keyed
    by the class object.

The webapp run-conformance stage 3 (Sinatra DSL) now RUNS green:
GET /hi?n=amy -> [200, headers, ["hi amy"]].

Registered after registerRack (Sinatra reuses Rack::Request/Response and needs
StandardError for Sinatra::NotFound). New binding code is at 100% coverage under
the -race -coverpkg CI gate (TZ=UTC); gofmt/vet clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit 500dd80 into main Jul 2, 2026
9 checks passed
@tannevaled
tannevaled deleted the bind/web branch July 2, 2026 22:00
tannevaled added a commit that referenced this pull request Jul 5, 2026
…oracle (#157)

The routing/dispatch core of go-ruby-sinatra is already bound (#104). This
lands two things a byte-for-byte diff against the real sinatra gem 4.2.1
surfaced:

1. Fix instance-variable persistence in the block-action bridge. Real Sinatra
   runs a request's before filter(s), route body and after filter(s) against a
   single instance, so an @ivar set in `before` (e.g. @user) is visible in the
   route and after blocks. The binding created a fresh SinatraCtx per action and
   SinatraCtx could not hold ivars at all (ivarTable returned nil for it), so
   `before { @uid = ... }` was silently dropped by the time the route ran. Give
   SinatraCtx an ivar table and reuse one SinatraCtx per request (keyed by the
   library's per-request *sinatra.Context, reset around each dispatch) so ivars
   persist within a request and do not leak across requests.

2. Add TestSinatraGemOracle: a run-conformance proof THROUGH rbgo that a real
   Sinatra app (apps/sinatra_oracle.rb: :name capture, query param, before-ivar,
   splat, optional param, redirect+Location, halt, custom error(code), custom
   not_found, after-filter) answers byte-for-byte identically to the sinatra gem
   4.2.1. The golden vector is captured from that gem run and embedded so the
   suite stays hermetic (no ruby/gem at CI time).

Refresh the stale webapp/doc.go (stage 3 now runs, not a gap).

Two response headers are excluded from the oracle diff and flagged as
go-ruby-sinatra LIBRARY follow-ups, not binding concerns: Content-Length
(go-ruby-rack does not finalize it) and X-Cascade: pass on 404s. The default
404 HTML page is likewise a library default (empty body) vs the gem's styled
page. 100% coverage on the binding.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant